package shaw.wifi.database;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.UUID;

public class DatabaseConnection {
	static {
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
	}
	
	private static DatabaseConnection instance = null;
	public static DatabaseConnection get() {
		if(instance == null) {
			instance = new DatabaseConnection();
		}
		return instance;
	}
	
	private Connection conn;
	
	private DatabaseConnection() {
		try {
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/radius", "radius", "xq7S23b_9");
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	private String insertQuery = "SELECT * FROM AppUsers WHERE username=? AND passwordSHA256=?;";
	public boolean verifyUser(String user, String password) {
		String sha256Password = sha256Pass(password);
		try {
			PreparedStatement stmt = conn.prepareStatement(insertQuery);
			stmt.setString(1, user);
			stmt.setString(2, sha256Password);
			if(stmt.executeQuery().last()) {
				return true;
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		return false;
	}
	
	public String[] generateUsernamePassword() {
		String uname = UUID.randomUUID().toString();
		String passwd = md5(String.valueOf(Math.random()*100000)).substring(0, 8);
		String hashPw = sha256Pass(passwd);
		
		String insertStr = "INSERT INTO AppUsers (username, passwordSHA256) VALUES ('" + uname + "', '" + hashPw + "');";
		System.out.println(insertStr);
		try {
			Statement query = conn.createStatement();
			query.execute(insertStr);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		String[] result = {uname, passwd};
		return result;
	}
	
	public boolean insertRadiusUsernamePasswordForUser(String peapUser, String peapPass, String user) {
		String pquery = "SELECT r_id FROM AppUsers WHERE username=?;";
		int rid = -1;
		System.out.println("looking for rid...");
		try {
			PreparedStatement ps = conn.prepareStatement(pquery);
			ps.setString(1, user);
			ps.execute();
			ps.getResultSet().last();
			rid = ps.getResultSet().getInt(1);
			System.out.println("user's rid: " + rid);
		} catch (SQLException e1) {
			e1.printStackTrace();
		}
		
		String[] userpass;
		if(user != null) {
			userpass = user.split("$");
		}
		
		
		String peapPassNtHash = ntPass(peapPass);
		if(rid == -1) {
			try {
				String query = "INSERT INTO radcheck (username, attribute, op, value) VALUES ('" +
					peapUser + "', '" +
					"NT-Password" + "', '" +
					":=" + "', '" +
					peapPassNtHash + "');";
				Statement s = conn.createStatement();
				System.out.println("executing query " + query);
				
				//update AppUsers table
				s.execute(query, Statement.RETURN_GENERATED_KEYS);
				ResultSet rs = s.getGeneratedKeys();
				
				rs.last();
				int r_id = rs.getInt(1);
				
				String update = "UPDATE AppUsers SET r_id='" + String.valueOf(r_id) +"' WHERE username='" + user + "';";
				Statement u = conn.createStatement();
				u.execute(update);
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {
			String update = "UPDATE radcheck SET username='" +
				peapUser + "', value='" + peapPassNtHash + "' WHERE id='" + String.valueOf(rid) + "';";
			try {
				Statement u = conn.createStatement();
				u.execute(update);
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
		return false;
	}
	
	public static String randomString() {
		String input = String.valueOf((long) (Math.random()*(double)Long.MAX_VALUE));
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			byte[] inBytes = input.getBytes("UTF-16LE");
			byte[] hash = md.digest(inBytes);
			
			BigInteger hashInt = new BigInteger("0");
			for(int i = 0; i<hash.length; i++) {
				int hashI = 0xFF & hash[i];
				
				hashInt = hashInt.add(new BigInteger(String.valueOf(hashI)));
				hashInt = hashInt.shiftLeft(8);
			}
			
			return hashInt.toString(16);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static String md5(String input) {
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			byte[] hash = md.digest(input.getBytes());
			return new String(hash);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	public static String ntPass(String input) {
		try {
			MessageDigest md = MessageDigest.getInstance("MD4", "BC");
			byte[] inBytes = input.getBytes("UTF-16LE");
			byte[] hash = md.digest(inBytes);
			
			BigInteger hashInt = new BigInteger("0");
			for(int i = 0; i<hash.length; i++) {
				int hashI = 0xFF & hash[i];
				
				hashInt = hashInt.add(new BigInteger(String.valueOf(hashI)));
				hashInt = hashInt.shiftLeft(8);
			}
			return hashInt.toString(16).substring(0, 32);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchProviderException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return null;
	}
	
	public static String sha256Pass(String input) {
		try {
			MessageDigest md = MessageDigest.getInstance("SHA256", "BC");
			byte[] inBytes = input.getBytes("UTF-16LE");
			byte[] hash = md.digest(inBytes);
			
			BigInteger hashInt = new BigInteger("0");
			for(int i = 0; i<hash.length; i++) {
				int hashI = 0xFF & hash[i];
				
				hashInt = hashInt.add(new BigInteger(String.valueOf(hashI)));
				hashInt = hashInt.shiftLeft(8);
			}
			return hashInt.toString(16).substring(0, 32);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchProviderException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return null;
	}
}
